package avicit.bdp.dms.perm.utils.common;

import avicit.bdp.common.service.dto.BdpTableDTO;
import avicit.bdp.common.service.dto.DataSourceDTO;
import avicit.bdp.common.service.service.DataSourceService;
import avicit.bdp.common.utils.ConfigUtils;
import avicit.bdp.common.utils.database.adapter.BaseDBAdapter;
import avicit.bdp.common.utils.database.adapter.DbAdapterUtils;
import avicit.bdp.core.constant.Constants;
import avicit.bdp.core.util.json.JSONUtils;
import avicit.bdp.dms.perm.dao.CategoryPermDAO;
import avicit.bdp.dms.perm.dao.DataPermDAO;
import avicit.bdp.dms.perm.dto.CategoryPermDTO;
import avicit.bdp.dms.perm.dto.DataPermDTO;
import avicit.bdp.dms.perm.service.DataSecretService;
import avicit.bdp.dms.perm.utils.enums.ColumnTypeEnum;
import avicit.platform6.api.system.ConvertColumnClient;
import avicit.platform6.api.system.SysLookupClient;
import avicit.platform6.api.system.SysRoleClient;
import avicit.platform6.api.system.SysUserClient;
import avicit.platform6.api.system.SysUserRoleClient;
import avicit.platform6.api.system.impl.SystemConstant;
import avicit.platform6.api.sysuser.dto.SysRole;
import avicit.platform6.api.sysuser.dto.SysUser;
import avicit.platform6.commons.utils.BusinessUtil;
import avicit.platform6.core.context.ThreadContextHelper;
import avicit.platform6.core.exception.BusinessException;
import com.alibaba.fastjson2.JSONObject;
import com.google.common.collect.Lists;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @金航数码科技有限责任公司
 * @作者：developer
 * @邮箱：developer@avic-digital.com
 * @创建时间： 2022-10-13
 * @类说明：PermCommonService
 * @修改记录：
 * @注意事项：
 * @主要功能：权限公共服务Service
 */
@Component
public class PermCommonService {
    private static final Logger LOGGER = LoggerFactory.getLogger(PermCommonService.class);

    /**
     * 模型分类授权DAO层
     */
    @Autowired
    private CategoryPermDAO CategoryPermDAO;

    /**
     * 行权限DAO层
     */
    @Autowired
    private DataPermDAO DataPermDAO;

    /**
     * 数据密级服务
     */
    @Autowired
    private DataSecretService dataSecretService;

    /**
     * V6平台用户服务
     */
    @Autowired
    private SysUserRoleClient sysUserRoleClient;
    @Autowired
    private SysUserClient sysUserClient;

    /**
     * V6平台通用代码
     */
    @Autowired
    private SysLookupClient sysLookupClient;

    @Autowired
    private ConvertColumnClient convertColumnClient;

    /**
     * V6平台角色服务
     */
    @Autowired
    private SysRoleClient sysRoleClient;

    @Autowired
    private DataSourceService dataSourceService;

    /**
     * 数据库类型转换
     */
    private static Map<ColumnTypeEnum, ColumnTypeEnum> typeMap;

    static {
        typeMap = new HashMap<>();

        typeMap.put(ColumnTypeEnum.TINYIINT, ColumnTypeEnum.INTEGER);
        typeMap.put(ColumnTypeEnum.FLOAT, ColumnTypeEnum.INTEGER);
        typeMap.put(ColumnTypeEnum.DECIMAL, ColumnTypeEnum.INTEGER);
        typeMap.put(ColumnTypeEnum.NUMBER, ColumnTypeEnum.INTEGER);
        typeMap.put(ColumnTypeEnum.SAMLLINT, ColumnTypeEnum.INTEGER);
        typeMap.put(ColumnTypeEnum.INT, ColumnTypeEnum.INTEGER);
        typeMap.put(ColumnTypeEnum.BIGINT, ColumnTypeEnum.INTEGER);
        typeMap.put(ColumnTypeEnum.DOUBLE, ColumnTypeEnum.INTEGER);
        typeMap.put(ColumnTypeEnum.DOUBLE, ColumnTypeEnum.INTEGER);
        typeMap.put(ColumnTypeEnum.MEDIUMINT, ColumnTypeEnum.INTEGER);
        typeMap.put(ColumnTypeEnum.INT2, ColumnTypeEnum.INTEGER);
        typeMap.put(ColumnTypeEnum.INT4, ColumnTypeEnum.INTEGER);
        typeMap.put(ColumnTypeEnum.INT8, ColumnTypeEnum.INTEGER);
        typeMap.put(ColumnTypeEnum.REAL, ColumnTypeEnum.INTEGER);
        typeMap.put(ColumnTypeEnum.NUMBERIC, ColumnTypeEnum.INTEGER);
        typeMap.put(ColumnTypeEnum.BYTEA, ColumnTypeEnum.INTEGER);
        typeMap.put(ColumnTypeEnum.FLOAT4, ColumnTypeEnum.INTEGER);
        typeMap.put(ColumnTypeEnum.FLOAT8, ColumnTypeEnum.INTEGER);
        typeMap.put(ColumnTypeEnum.INTEGER, ColumnTypeEnum.INTEGER);
        typeMap.put(ColumnTypeEnum.INT32, ColumnTypeEnum.INTEGER);
        typeMap.put(ColumnTypeEnum.INT64, ColumnTypeEnum.INTEGER);
        typeMap.put(ColumnTypeEnum.CHAR, ColumnTypeEnum.STRING);
        typeMap.put(ColumnTypeEnum.VARCHAR2, ColumnTypeEnum.STRING);
        typeMap.put(ColumnTypeEnum.NCHAR, ColumnTypeEnum.STRING);
        typeMap.put(ColumnTypeEnum.NVARCHAR2, ColumnTypeEnum.STRING);
        typeMap.put(ColumnTypeEnum.VARCHAR, ColumnTypeEnum.STRING);
        typeMap.put(ColumnTypeEnum.BLOB, ColumnTypeEnum.STRING);
        typeMap.put(ColumnTypeEnum.CLOB, ColumnTypeEnum.STRING);
        typeMap.put(ColumnTypeEnum.DATE, ColumnTypeEnum.STRING);
        typeMap.put(ColumnTypeEnum.TIMESTAMP, ColumnTypeEnum.STRING);
        typeMap.put(ColumnTypeEnum.TEXT, ColumnTypeEnum.STRING);
        typeMap.put(ColumnTypeEnum.MEDIUMTEXT, ColumnTypeEnum.STRING);
        typeMap.put(ColumnTypeEnum.LONGTEXT, ColumnTypeEnum.STRING);
        typeMap.put(ColumnTypeEnum.TIME, ColumnTypeEnum.STRING);
        typeMap.put(ColumnTypeEnum.YEAR, ColumnTypeEnum.STRING);
        typeMap.put(ColumnTypeEnum.DATETIME, ColumnTypeEnum.STRING);
        typeMap.put(ColumnTypeEnum.STRING, ColumnTypeEnum.STRING);
    }

    /**
     * 根据通用编码类型获取通用编码具体内容
     *
     * @param userSecret
     * @return
     */
    public List<String> getSecretWordList(String userSecret) {
        Collection<String> lookupSimpleVoList = this.sysLookupClient.getSecretWordList(userSecret);

        return new ArrayList<>(lookupSimpleVoList);
    }

    /**
     * 获取当前登录用户信息
     *
     * @param id
     * @return
     */
    public SysUser getSysUserInfoById(String id) {
        return this.sysUserClient.getSysUserById(id);
    }

    /**
     * 根据用户ID获取用户名；传入一个或多个userId，返回userName
     *
     * @param userIds
     * @return
     * @throws Exception
     */
    public String getUserNameByIds(String userIds) {
        if (avicit.platform6.commons.utils.StringUtils.isEmpty(userIds)) {
            return null;
        }

        Map<String, Set<String>> convertFormData = new HashMap<>(16);
        String[] array = userIds.split(Constants.COMMA);
        for (String userId : array) {
            BusinessUtil.createConvertSet(convertFormData, SystemConstant.USER, userId);
        }

        StringBuffer buf = new StringBuffer("");
        if (convertFormData.size() > 0) {
            //获取请求结果
            Map<String, Map<String, String>> convertResultData = convertColumnClient.replace(convertFormData);
            for (String userId : array) {
                String name = BusinessUtil.convertFormat(convertResultData, SystemConstant.USER, userId);
                buf.append(name).append(Constants.COMMA);
            }
        }

        String names = buf.toString();
        if (names.length() >= 1) {
            names = names.substring(0, names.length() - 1);
        }
        return names;
    }

    /**
     * 根据用户ID获取角色列表
     *
     * @param loginUserId
     * @return
     */
    public List<String> getRoleListByUserId(String loginUserId) {
        if (StringUtils.isBlank(loginUserId)) {
            return new ArrayList<>();
        }

        List<SysRole> sysRoleList = this.sysUserRoleClient.getSysRoleListBySysUserIdNoAppId(loginUserId);
        if (CollectionUtils.isNotEmpty(sysRoleList)) {
            return sysRoleList.stream().map(SysRole::getId).collect(Collectors.toList());
        }

        return new ArrayList<>();
    }

    /**
     * 获取授权角色ID列表
     *
     * @return
     */
    public List<String> getAuthRoleIdList() {
        String confAuthRoles = ConfigUtils.getInstance().getString(Constants.SYSTEM_AUTH_ROLE);
        if (StringUtils.isBlank(confAuthRoles)) {
            throw new BusinessException("请首先在系统中配置授权角色（多个角色编码使用英文逗号格卡）!");
        }

        List<SysRole> sysAllRoleList = this.sysRoleClient.getAllSysRoles();
        if (CollectionUtils.isEmpty(sysAllRoleList)) {
            throw new BusinessException("系统中未配置角色!");
        }

        List<String> authRoleIdList = Lists.newArrayList();
        List<String> roleCodes = Arrays.asList(confAuthRoles.trim().split(Constants.COMMA));
        for (String roleCode : roleCodes) {
            if (StringUtils.isBlank(roleCode)) {
                continue;
            }

            for (SysRole sysRole : sysAllRoleList) {
                if (sysRole != null && sysRole.getRoleCode() != null
                        && sysRole.getId() != null && sysRole.getRoleCode().equals(roleCode)) {
                    authRoleIdList.add(sysRole.getId());
                }
            }
        }

        return authRoleIdList;
    }

    /**
     * 当前登录角色是否为授权角色
     *
     * @return
     */
    public boolean isAuthRole() {
        List<String> loginRoleIdList = getRoleListByUserId(ThreadContextHelper.getUserId());
        List<String> authRoleIdList = getAuthRoleIdList();

        return !Collections.disjoint(loginRoleIdList, authRoleIdList);
    }

    /**
     * 根据模型分类ID删除权限（删除模型分类节点时删除权限，DataCategoryService类中调用)
     *
     * @param nodeId
     */
    public void deleteCategoryPerm(String nodeId) {
        this.CategoryPermDAO.deletePermByCond(null, nodeId, null);
    }

    /**
     * 根据模型ID删除权限（删除模型时删除权限，BdpDasTableService类中调用）
     *
     * @param tableId
     */
    public void deleteDataPerm(String tableId) {
        this.DataPermDAO.deletePermByCond(null, tableId);
    }

    /**
     * 根据角色获取所有权限（包括查看、编辑）
     *
     * @param roleIdList
     * @return
     */
    public List<CategoryPermDTO> getCategoryPermListByRoles(List<String> roleIdList) {
        return this.CategoryPermDAO.getAllPermListByRoles(roleIdList);
    }

    /**
     * 根据角色获取数据查看权限
     *
     * @param roleIdList
     * @return
     */
    public List<CategoryPermDTO> getCategoryDataViewPermListByRoles(List<String> roleIdList) {
        return this.CategoryPermDAO.getDataViewPermListByRoles(roleIdList);
    }

    /**
     * 授权角色查询指定角色的权限（只包含授权角色授予的权限）
     *
     * @param roleId
     * @return
     */
    public List<CategoryPermDTO> getCategoryGrantedPermList(String roleId) {
        return this.CategoryPermDAO.getGrantedPermList(roleId);
    }

    /**
     * 拼接指定模型表查询SQL（根据权限和密级进行过滤）
     *
     * @param tableDTO
     * @param isHaveSecretLevelWord 模型表中是否有密级字段
     * @return
     */
    public String splicingSelectSql(BdpTableDTO tableDTO, String paramJson, boolean isHaveSecretLevelWord) {
        String sql = "";
        if (tableDTO == null || StringUtils.isBlank(tableDTO.getId())) {
            return null;
        }

        // step1:根据登录用户角色，获取行权限列表
        List<String> roleIdList = getRoleListByUserId(ThreadContextHelper.getUserId());
        List<DataPermDTO> permList = this.DataPermDAO.getDataPermByTable(roleIdList, tableDTO.getId());

        // step2:无权限也需要设置密级信息
        if (CollectionUtils.isEmpty(permList)) {
            sql = buildQuerySql(tableDTO, null, isHaveSecretLevelWord);
        }

        // step3:根据行权限拼接查询SQL（一个用户具有多角色，其属多个角色同时对一张表设置了权限，以最后一个更新的为准）
        else {
            permList.sort(Comparator.comparing(DataPermDTO::getLastUpdateDate).reversed());
            sql = buildQuerySql(tableDTO, permList.get(0), isHaveSecretLevelWord);
        }
        if (StringUtils.isNotBlank(paramJson)) {
            sql = HandleSql(sql, paramJson, tableDTO);
        }
        LOGGER.info(">>>>>>>>>>>>>>>sql={}<<<<<<<<<<<<<<<", sql);
        return sql;
    }

    private String HandleSql(String sql, String paramJson, BdpTableDTO tableDTO) {
        //解析json
        Map<String, Object> jsonMap = JSONObject.parseObject(paramJson);
        List<Map<String, Object>> mapList = (List<Map<String, Object>>) jsonMap.get("param");
        if (CollectionUtils.isNotEmpty(mapList)) { //如果有条件，判断是否加where
            if (sql.contains("where")) {
                sql = sql + " and " + buildQueryParamSql(tableDTO, paramJson);
            } else {
                sql = sql + " where " + buildQueryParamSql(tableDTO, paramJson);
            }
        } else {
            sql = sql + buildQueryParamSql(tableDTO, paramJson);
        }
        return sql;
    }

    private String buildQueryParamSql(BdpTableDTO tableDTO, String paramJson) {
        String datasourceId = tableDTO.getDataSourceId();
        DataSourceDTO dataSourceDTO = dataSourceService.selectByPrimaryKey(datasourceId);
        String datasourceType = dataSourceDTO.getDatasourceType();
        BaseDBAdapter dbAdapter = DbAdapterUtils.getDbAdapter(datasourceType);
        //解析json
        Map<String, Object> jsonMap = JSONObject.parseObject(paramJson);
        List<Map<String, Object>> mapList = (List<Map<String, Object>>) jsonMap.get("param");
        Map<String, Object> orderMap = (Map<String, Object>) jsonMap.get("sort");
        StringBuilder whereSql = new StringBuilder();
        if (CollectionUtils.isNotEmpty(mapList)) {
            int count = 0;
            for (Map<String, Object> stringObjectMap : mapList) {
                count++;
                String name = MapUtils.getString(stringObjectMap, "name");
                String condition = MapUtils.getString(stringObjectMap, "condition");
                String type = MapUtils.getString(stringObjectMap, "type");
                String value = MapUtils.getString(stringObjectMap, "value");
                Boolean flag = dbAdapter.getNumberType().contains(type.toLowerCase());
                whereSql.append(" " + name).append(" " + condition + " ");
                if ("in".equals(condition)) {
                    StringBuilder values = new StringBuilder();
                    values.append("(");
                    String[] vals = value.split(",");
                    int valcount = 0;
                    for (String val : vals) {
                        valcount++;
                        if (!flag) {
                            values.append("'");
                        }
                        values.append(val);
                        if (!flag) {
                            values.append("'");
                        }
                        if (valcount < vals.length) {
                            values.append(",");
                        }
                    }
                    values.append(")");
                    whereSql.append(values);
                } else if ("like".equals(condition)) {
                    whereSql.append("'%" + value + "%'");
                } else {
                    if ("oracle".equals(datasourceType.toLowerCase())) {
                        if ("date".equals(type.toLowerCase())) {
                            whereSql.append("to_date('" + value + "', 'yyyy-mm-dd hh24:mi:ss')");
                        } else if ("timestamp".equals(type.toLowerCase())) {
                            whereSql.append("to_timestamp('" + value + "', 'yyyy-mm-dd hh24:mi:ss')");
                        } else {
                            if (!flag) {
                                whereSql.append("'" + value + "'");
                            } else {
                                whereSql.append(value);
                            }
                        }
                    } else {
                        if (!flag) {
                            whereSql.append("'" + value + "'");
                        } else {
                            whereSql.append(value);
                        }
                    }
                }
                if (count < mapList.size()) {
                    whereSql.append(" and ");
                }
            }
        }
        if (orderMap != null) {
            String orderName = MapUtils.getString(orderMap, "name");
            if (StringUtils.isNotBlank(orderName)) {
                String orderType = MapUtils.getString(orderMap, "type");
                whereSql.append(" order by " + orderName + " " + orderType);
            }
        }
        return whereSql.toString();
    }

    /**
     * 根据行权限构建查询SQL
     *
     * @param bdpTableDTO
     * @param dataPermDTO
     * @param isHaveSecretLevelWord 模型表中是否有密级字段
     * @return
     */
    private String buildQuerySql(BdpTableDTO bdpTableDTO, DataPermDTO dataPermDTO, boolean isHaveSecretLevelWord) {
        StringBuilder sql = new StringBuilder();

        // 权限过滤
        if (dataPermDTO != null) {
            // 受控字段
            String columns = dataPermDTO.getColumns();
            if (StringUtils.isNotBlank(columns)) {
                List<String> columnList = JSONUtils.toList(columns, String.class);
                sql.append("select ").append(String.join(",", columnList)).append(" from " + bdpTableDTO.getName());
            } else {
                sql.append("select * from " + bdpTableDTO.getName());
            }

            // where自定义SQL
            if (StringUtils.isNotBlank(dataPermDTO.getCustomWhereCond())) {
                sql.append(" where " + dataPermDTO.getCustomWhereCond());
            }
        } else {
            sql.append("select * from " + bdpTableDTO.getName());
        }

        // 密级未开启，SQL中不需要secret_level
        String userSecretLevel = this.dataSecretService.getDataSecretCond();
        if (StringUtils.isBlank(userSecretLevel)) {
            LOGGER.info(">>>>>>>>>>>>>>>sql={}<<<<<<<<<<<<<<<", sql.toString());

            return sql.toString();
        }

        // 密级开启 && 模型表中有secret_level密级字段，根据密级过滤
        if (isHaveSecretLevelWord) {
            if (sql.toString().contains("where")) {
                sql.append(String.format(" and find_in_set(%s.secret_level,'%s')", bdpTableDTO.getName(), userSecretLevel));
            } else {
                sql.append(String.format(" where find_in_set(%s.secret_level,'%s')", bdpTableDTO.getName(), userSecretLevel));
            }
        } else {
            // 密级开启 && 模型表中没有有secret_level密级字段，不做处理
            ;
        }

        LOGGER.info(">>>>>>>>>>>>>>>sql={}<<<<<<<<<<<<<<<", sql.toString());

        return sql.toString();
    }
}
